#include <string.h>
#include <stdlib.h>

#ifdef _MSC_VER
#else
#include <unistd.h>
#include <sys/mman.h>
#endif

#include "ring_streams.h"
#include "retcodes.h"
#include "base.h"

namespace common
{

	static const uint32_t kMinBufferSize = 1 << 12;

	CRingStreams::CRingStreams()
	{
		buffer_ = NULL;
		capacity_ = 0;
		read_index_ = 0;
		write_index_ = 0;
	}

	CRingStreams::~CRingStreams()
	{
		if (capacity_ != 0)
		{
			Finalize();
		}
	}

	int32_t CRingStreams::Initialize(uint32_t capacity)
	{
#ifdef _MSC_VER
		read_index_ = 0;
		write_index_ = 0;
		capacity_ = TCP_PIPE_SIZE;
		buffer_ = new uint8_t[TCP_PIPE_SIZE];
		memset(buffer_, 0, TCP_PIPE_SIZE);
		return ::common::SUCCEED;
#else
		if (capacity_ != 0)
		{
			return ERINGSTREAM_ALREADY_INIT;
		}
		char path[] = "/dev/shm/ring-buffer-XXXXXX";
		int fd = mkstemp(path);
		if (fd < 0)
		{
			return ERINGSTREAM_MKSTEMP_FAIL;
		}
		int status = unlink(path);
		if (status != 0)
		{
			return ERINGSTREAM_UNLINK_FAIL;
		}
		capacity_ = RoundupPowOfTwo(capacity);
		if (capacity_ < kMinBufferSize)
		{
			capacity_ = kMinBufferSize;
		}
		read_index_ = 0;
		write_index_ = 0;
		if (ftruncate(fd, capacity_) != 0)
		{
			return ERINGSTREAM_FTRUNCATE_FAIL;
		}
		buffer_ = (uint8_t*)mmap(NULL, capacity_ << 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
		if (buffer_ == MAP_FAILED)
		{
			return ERINGSTREAM_MAP_FAIL1;
		}
		void* address = mmap(buffer_, capacity_, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
		if (address != buffer_)
		{
			return ERINGSTREAM_MAP_FAIL2;
		}
		address = mmap(buffer_ + capacity_, capacity_, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0);
		if (address != buffer_ + capacity_)
		{
			return ERINGSTREAM_MAP_FAIL3;
		}
		if (close(fd) != 0)
		{
			return ERINGSTREAM_CLOSE_FD_FAIL;;
		}
#endif
		return ::common::SUCCEED;
	}

	int32_t CRingStreams::Finalize()
	{
#ifdef _MSC_VER
		delete[] buffer_;
#else
		int status = munmap(buffer_, capacity_ << 1);
		if (status != 0)
		{
			return ERINGSTREAM_MUNMAP_FAIL;
		}
#endif
		read_index_ = 0;
		write_index_ = 0;
		capacity_ = 0;
		return ::common::SUCCEED;
	}

	uint32_t CRingStreams::Size() const
	{
		return write_index_ - read_index_;
	}

	uint32_t CRingStreams::FreeSize() const
	{
		return capacity_ - Size();
	}

	int32_t CRingStreams::Clear()
	{
		read_index_ = 0;
		write_index_ = 0;
		return ::common::SUCCEED;
	}

	void CRingStreams::AddWriteBufSize(uint32_t size)
	{
		write_index_ += size;
	}

	void CRingStreams::AddReadBufSize(uint32_t size)
	{
		read_index_ += size;
		if (read_index_ >= capacity_)
		{
			read_index_ -= capacity_;
			write_index_ -= capacity_;
		}
	}

	uint8_t* CRingStreams::ReadBuffer() const
	{
		return &buffer_[read_index_];
	}

	uint8_t* CRingStreams::WriteBuffer()
	{
		return &buffer_[write_index_];
	}

	uint32_t CRingStreams::capacity() const
	{
		return capacity_;
	}
}
